import math
import random
import time
from typing import List, Dict, Any, Optional, Tuple


def _ease_out_cubic(p: float) -> float:
    return 1 - (1 - p) ** 3


def _rand_between(a: int, b: int) -> int:
    return random.randint(a, b)


def _choice(seq):
    return random.choice(seq)


def build_random_tq_object(
    x: int,
    y: int,
    tqdna: str,
    *,
    start_time: Optional[int] = None,
    duration_ms: Optional[int] = None,
    steps_range: Tuple[int, int] = (80, 140),
    # 停顿配置：每次停顿的概率与时长范围（毫秒）
    pause_prob: float = 0.12,
    pause_ms_range: Tuple[int, int] = (120, 800),
    micro_pause_prob: float = 0.18,
    micro_pause_ms_range: Tuple[int, int] = (14, 40),
    # 每步基本时间间隔范围（毫秒）
    step_tm_range: Tuple[int, int] = (7, 12),
    # 是否在末尾加入水平微进段
    tail_micro_steps: int = 10,
    # 速度小数位
    speed_decimals: int = 4,
    # 是否生成一次点击
    with_click: bool = False,
    # 随机种子（可复现）
    seed: Optional[int] = None,
    # 设备/屏幕/视口覆盖
    browser: Optional[Dict[str, Any]] = None,
    osinfo: Optional[Dict[str, Any]] = None,
    screen: Optional[Dict[str, Any]] = None,
    viewport: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
    """
    随机生成一个与示例结构一致的对象：
    - trail：随机长度（steps_range），包含 x,y,t,s,tm,isd；
    - hover：从轨迹时间中采样生成；
    - click：可选，点击在 (x,y)；
    - tqdna：使用传入值；
    - startTime/endTime/t：时间按生成轨迹求得；若传 duration_ms 则尽量匹配。
    """

    if seed is not None:
        random.seed(seed)

    # 1) 时间基准
    if start_time is None:
        # 让时间看起来像“现在”的毫秒时间附近
        base_now = int(time.time() * 1000)
        # 给一个小偏移，避免与系统时间相同
        start_time = base_now - _rand_between(500, 1500)

    # 2) 决定轨迹点数
    steps = _rand_between(*steps_range)

    # 3) 决定起点（从目标点左上或右上起步）
    #    根据 (x,y) 反推一个较远的起点，强度随机
    horiz_dir = _choice([-1, 1])  # 左上或右上
    x_offset = horiz_dir * _rand_between(200, 900)
    y_offset = -_rand_between(150, 600)
    x0 = x + x_offset
    y0 = max(0, y + y_offset)

    # 4) 生成每步时间间隔 tm 分布（混合正常步与停顿）
    tm_list: List[int] = []
    total_time = 0
    long_pauses_idx = set()
    micro_pauses_idx = set()

    for i in range(steps):
        # 基本步进时间
        tm = _rand_between(*step_tm_range)

        # 随机插入停顿：概率控制
        if random.random() < pause_prob and 5 < i < steps - 10:
            tm = _rand_between(*pause_ms_range)
            long_pauses_idx.add(i)
        elif random.random() < micro_pause_prob and 3 < i < steps - 5:
            tm = _rand_between(*micro_pause_ms_range)
            micro_pauses_idx.add(i)

        tm_list.append(tm)
        total_time += tm

    # 若用户指定了大致时长，做一次比例调整（避免漂移太大）
    if duration_ms is not None and total_time > 0:
        scale = max(0.6, min(1.6, duration_ms / total_time))
        # 对 tm 做轻微缩放，避免破坏停顿分布
        new_tm_list = []
        for i, tm in enumerate(tm_list):
            if i in long_pauses_idx:
                new_tm_list.append(int(round(tm * scale)))  # 长停顿可缩放
            else:
                new_tm_list.append(max(1, int(round(tm * scale))))
        tm_list = new_tm_list
        total_time = sum(tm_list)

    # 5) 累加得到每个节点的 t 值
    t_values: List[int] = []
    acc = start_time
    for tm in tm_list:
        acc += tm
        t_values.append(acc)

    # 6) 生成轨迹坐标：加速-匀速-减速 + 停顿后微抖
    #    将 steps 映射为进度 p ∈ [0,1]，使用 ease 函数
    xs: List[int] = []
    ys: List[int] = []

    for i in range(steps):
        p_global = (i + 1) / steps
        # 三段式：前 45% 加速，中间 35% 匀速，后 20% 减速
        if p_global < 0.45:
            p_seg = p_global / 0.45
            p = 0.45 * _ease_out_cubic(p_seg)
        elif p_global < 0.80:
            p_seg = (p_global - 0.45) / 0.35
            p = 0.45 + 0.35 * p_seg
        else:
            p_seg = (p_global - 0.80) / 0.20
            p = 0.80 + 0.20 * (1 - (1 - p_seg) ** 2)  # 轻缓减速

        xi = x0 + (x - x0) * p
        yi = y0 + (y - y0) * (p**0.9)  # y 稍早收敛，模仿斜向轨迹
        xs.append(int(round(xi)))
        ys.append(int(round(yi)))

    # 7) 在停顿后进行小幅抖动与回拉，让“手感”更像人工
    for i in range(steps):
        if i in long_pauses_idx:
            # 长停顿后的一小段，增加微扰
            for k in range(1, _rand_between(2, 4)):
                j = i + k
                if j < steps:
                    xs[j] += _choice([-2, -1, 0, 1, 2])
                    ys[j] += _choice([-2, -1, 0, 1, 2])

    # 8) 尾段水平微进（靠近目标时的小步）
    for j in range(steps - tail_micro_steps, steps):
        if j <= 0 or j >= steps:
            continue
        xs[j] = xs[j - 1] + (1 if xs[j] <= xs[j - 1] else 0)
        # y 小范围跟随
        dy = ys[j] - ys[j - 1]
        if abs(dy) <= 1:
            ys[j] = ys[j - 1]
        else:
            ys[j] = ys[j - 1] + (1 if dy > 0 else -1)

    # 9) 最后一个点锚定到 (x, y)
    xs[-1] = x
    ys[-1] = y

    # 10) 计算速度 s = dist / tm，并构造 trail
    trail: List[Dict[str, Any]] = []
    prev_x, prev_y = None, None
    for i in range(steps):
        xi, yi = xs[i], ys[i]
        tm = tm_list[i]
        ti = t_values[i]

        if prev_x is None:
            # 第一段给一个正的、合理的 s
            dist = max(1.0, math.hypot(xi - x0, yi - y0) / max(5, steps))
        else:
            dist = math.hypot(xi - prev_x, yi - prev_y)

        s_val = 0.0 if tm == 0 else round(dist / tm, speed_decimals)

        # 对超长停顿用极小速度值，让“风格”更逼真
        if tm >= 120:
            # 极小且随机的“抖动速度”
            s_val = round(
                _choice(
                    [0.0039, 0.004, 0.0067, 0.0076, 0.0104, 0.0132, 0.0133, 0.0139]
                ),
                4,
            )

        trail.append({"x": xi, "y": yi, "t": ti, "s": s_val, "tm": tm, "isd": 0})
        prev_x, prev_y = xi, yi

    # 11) 生成 hover：从部分节拍中抽样，元素名从候选中挑选
    hover_elems = [
        "div.verify-move-block",
        "div.verify-bar-area",
        "div.verify-img-out",
        "div",
        "i.verify-icon.iconfont.icon-right",
    ]
    hover: List[Dict[str, Any]] = []
    # 取若干个锚点（按时间排序）
    hover_count = _rand_between(8, 12)
    candidate_idx = sorted(random.sample(range(5, steps - 1), hover_count))
    for idx in candidate_idx:
        hover.append(
            {
                "el": _choice(hover_elems),
                "dur": _rand_between(3, 48),
                "t": t_values[idx],
            }
        )

    # 12) click：可选地添加一次点击到 (x,y)；时间在结束前后
    click: List[Dict[str, Any]] = []
    if with_click:
        click.append({"x": x, "y": y, "t": t_values[-1] + _rand_between(100, 2000)})

    # 13) 设备信息与屏幕信息（可覆盖）
    browser_out = {"name": "Edge", "version": "139.0.0.0"}
    if isinstance(browser, dict):
        browser_out.update(browser)

    os_out = {"name": "Windows", "version": "10"}
    if isinstance(osinfo, dict):
        os_out.update(osinfo)

    screen_out = {
        "resolution": {"width": 1920, "height": 1080},
        "colorDepth": 24,
        "pixelDepth": 24,
        "availableResolution": {"width": 1920, "height": 1032},
    }
    if isinstance(screen, dict):
        # 浅合并（必要时深合并）
        screen_out.update(screen)

    viewport_out = {"width": 640, "height": 954}
    if isinstance(viewport, dict):
        viewport_out.update(viewport)

    # 14) endTime 与 t（结束时间）：使用轨迹最后时间，稍作抬升
    end_time = t_values[-1] + _rand_between(0, 5)

    return {
        "trail": trail,
        "click": click,
        "hover": hover,
        "slider": [],
        "startTime": start_time,
        "endTime": end_time,
        "browser": browser_out,
        "os": os_out,
        "screen": screen_out,
        "viewport": viewport_out,
        "tqdna": tqdna,
        "t": end_time,
    }


# 使用示例
# obj = build_random_tq_object(630, 303, "102-xxxx...", with_click=False, seed=123)
# print(len(obj["trail"]), "trail points")
# print(obj["trail"][:5])
# print(obj["hover"])
# 使用示例
obj = build_random_tq_object(
    1,
    2,
    "102-63a1d8e874d1bca53c15405b083527ee854a39449de6ebd88744502aab9933bb3017fb68689E9BBCe11384e862c35a98",
)
print(obj)
